﻿#include "asio_s2s_session.h"

#include <boost/asio/ip/tcp.hpp>  // for resolver
#include <boost/asio/connect.hpp>  // for async_connect()

#include "log.h"
#include "server_mailbox.h"  // for GetRemoteAddr()
#include "asio_server.h"

const char LOG_NAME[] = "CAsioS2sSession";

CAsioS2sSession::CAsioS2sSession(
	CAsioServer& rServer, CServerMailBox& rSvrMailBox) :
	CAsioSessionBase(rServer),
	m_rSvrMb(rSvrMailBox)
{

}

CAsioS2sSession::~CAsioS2sSession()
{
}

void CAsioS2sSession::Go()
{
	using boost::asio::spawn;
	using std::bind;
	using std::placeholders::_1;
	std::shared_ptr<CAsioS2sSession> pMe = shared_from_this();
	spawn(m_strand, bind(&CAsioS2sSession::LoopRead, pMe, _1));
	spawn(m_strand, bind(&CAsioS2sSession::LoopWrite, pMe, _1));
	LOG_DEBUG_TO("CAsioS2sSession.Go",
		Fmt("Start server mail box, SvrId=%u, Remote=%s:%u, MbId=%u.")
		% m_rSvrMb.GetServerId()
		% m_rSvrMb.GetRemoteAddr()
		% m_rSvrMb.GetRemotePort()
		% m_rSvrMb.GetId());
}

void CAsioS2sSession::LoopRead(boost::asio::yield_context yield)
{
	for (;;)
	{
		// Wait for (re)connected.
		while (!m_bConnected)
			WaitMs(1, yield);

		bool ok = ReadOne(*m_pSocket, yield);
		if (!ok)
			Disconnect();  // to reconnect
	}
}

void CAsioS2sSession::LoopWrite(boost::asio::yield_context yield)
{
	for (;;)
	{
		if (!m_pSocket)
		{
		Reconnect(yield);
		continue;
		}

		if (m_rSvrMb.IsEmpty())
		{
			WaitMs(1, yield);  // to yield
			continue;
		}

		bool ok = SendRpcs(*m_pSocket, m_rSvrMb, yield);
		if (!ok)
			Disconnect();
	}
}

void CAsioS2sSession::Reconnect(boost::asio::yield_context yield)
{
	assert(!m_bConnected);
	assert(!m_pSocket);

	boost::system::error_code ec;
	using resolver = boost::asio::ip::tcp::resolver;
	resolver rslvr(m_strand.get_io_service());
	const std::string& sSvrAddr = m_rSvrMb.GetRemoteAddr();
	uint16_t uSvrPort = m_rSvrMb.GetRemotePort();
	std::ostringstream oss;
	oss << uSvrPort;
	resolver::query qury(sSvrAddr, oss.str());
	resolver::iterator iter = rslvr.async_resolve(qury, yield[ec]);
	if (ec)
	{
		LOG_WARN_TO("CAsioS2sSession.Reconnect",
			Fmt("Failed to resolve: %s:%u, (%d)%s")
			% sSvrAddr % uSvrPort % ec.value() % ec.message());
		WaitASec(yield);
		return;
	}

	m_pSocket.reset(new socket(m_strand.get_io_service()));
	boost::asio::async_connect(*m_pSocket, iter, yield[ec]);
	if (!ec)
	{
		LOG_INFO_TO("CAsioS2sSession.Reconnect", "Connected. ip: " << sSvrAddr << ", port: " << uSvrPort); 
		m_bConnected = true;
		// 暂时没用到 CreateLinkHandler();
		return;
	}
	m_pSocket.reset();

	WaitASec(yield);
}



// Wait 1s
void CAsioS2sSession::WaitASec(boost::asio::yield_context yield)
{
	WaitMs(1000, yield);
}

void CAsioS2sSession::Disconnect()
{
	LOG_INFO_TO("CAsioS2sSession.Disconnect", Fmt("Remote: %s:%u")
		% m_rSvrMb.GetRemoteAddr() % m_rSvrMb.GetRemotePort());
	m_bConnected = false;
	m_pSocket.reset();
}

CMailBox& CAsioS2sSession::GetMailBox()
{
	return m_rSvrMb;
}

const CMailBox& CAsioS2sSession::GetMailBox() const
{
	return m_rSvrMb;
}

// 暂时没用到
// Todo: Session 中不要涉及逻辑， 应该移到别处。
//void CAsioS2sSession::CreateLinkHandler()
//{
//	//注册服务器
//	svr::RegisterRequest req;
//	req.set_serverid(m_rServer.GetServerId());
//	EServerType clientType = m_rServer.GetServerType();
//	EServerType serverType = m_rSvrMb.GetServerType();
//
//	if ((SERVER_MANAGER == serverType) || (SERVER_MANAGER_SLAVERY == serverType))
//	{
//		m_rSvrMb.PushRpcReq("svr.RegisterServer", "RegisterServer", req, [](const std::string& s) {
//			svr::RegisterResponse response;
//			if (!response.ParseFromString(s))
//			{
//				LOG_INFO("Parse RegisterResponse failed!");
//				return;
//			}
//			LOG_INFO("s2s register manager or slavery manager ok, remote serverid = " << response.serverid());
//		});
//	}
//	else if (SERVER_CELL == serverType)
//	{
//		if (SERVER_BASE == clientType)
//		{
//			m_rSvrMb.PushRpcReq("svr.RegisterServer", "RegisterServer", req, [](const std::string& s) {
//				svr::RegisterResponse response;
//				if (!response.ParseFromString(s))
//				{
//					LOG_INFO("Parse RegisterResponse failed!");
//					return;
//				}
//				LOG_INFO("s2s register cell ok, remote serverid =  " << response.serverid());
//			});
//		}
//	}
//}
